package com.github.dreamhead.moco.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.github.dreamhead.moco.HttpMethod;
import com.github.dreamhead.moco.HttpProtocolVersion;
import com.github.dreamhead.moco.HttpRequest;
import com.github.dreamhead.moco.extractor.CookiesRequestExtractor;
import com.github.dreamhead.moco.extractor.FormsRequestExtractor;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.QueryStringEncoder;
import java.util.List;
import java.util.Map;
import static com.github.dreamhead.moco.model.MessageContent.content;
import static com.google.common.collect.ImmutableMap.copyOf;
@JsonDeserialize(builder = DefaultHttpRequest.Builder.class)
public final class DefaultHttpRequest extends DefaultHttpMessage implements HttpRequest {
private final Supplier<ImmutableMap<String, String>> formSupplier;
private final Supplier<ImmutableMap<String, String>> cookieSupplier;
private final HttpMethod method;
private final String uri;
private final ImmutableMap<String, String[]> queries;
private DefaultHttpRequest(final HttpProtocolVersion version, final MessageContent content,
final HttpMethod method, final String uri,
final ImmutableMap<String, String> headers,
final ImmutableMap<String, String[]> queries) {
super(version, content, headers);
this.method = method;
this.uri = uri;
this.queries = queries;
this.formSupplier = formSupplier();
this.cookieSupplier = cookieSupplier();
}
@Override
public HttpMethod getMethod() {
return method;
}
@Override
public String getUri() {
return uri;
}
@JsonIgnore
public ImmutableMap<String, String> getForms() {
return formSupplier.get();
}
@JsonIgnore
public ImmutableMap<String, String> getCookies() {
return cookieSupplier.get();
}
@Override
public ImmutableMap<String, String[]> getQueries() {
return queries;
}
private Supplier<ImmutableMap<String, String>> formSupplier() {
return Suppliers.memoize(new Supplier<ImmutableMap<String, String>>() {
@Override
public ImmutableMap<String, String> get() {
Optional<ImmutableMap<String, String>> forms =
new FormsRequestExtractor().extract(DefaultHttpRequest.this);
return toResult(forms);
}
});
}
private ImmutableMap<String, String> toResult(final Optional<ImmutableMap<String, String>> result) {
if (result.isPresent()) {
return result.get();
}
return ImmutableMap.of();
}
private Supplier<ImmutableMap<String, String>> cookieSupplier() {
return Suppliers.memoize(new Supplier<ImmutableMap<String, String>>() {
@Override
public ImmutableMap<String, String> get() {
Optional<ImmutableMap<String, String>> cookies =
new CookiesRequestExtractor().extract(DefaultHttpRequest.this);
return toResult(cookies);
}
});
}
@Override
public String toString() {
return MoreObjects.toStringHelper("HTTP Request")
.omitNullValues()
.add("uri", this.uri)
.add("version", this.getVersion())
.add("method", this.method)
.add("queries", this.queries)
.add("headers", this.getHeaders())
.add("content", this.getContent())
.toString();
}
public static Builder builder() {
return new Builder();
}
private static MessageContent toMessageContent(final FullHttpRequest request) {
long contentLength = HttpUtil.getContentLength(request, -1);
if (contentLength <= 0) {
return content().build();
}
return content()
.withCharset(HttpUtil.getCharset(request))
.withContent(new ByteBufInputStream(request.content()))
.build();
}
public static HttpRequest newRequest(final FullHttpRequest request) {
QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
ImmutableMap<String, String[]> queries = toQueries(decoder);
return builder()
.withVersion(HttpProtocolVersion.versionOf(request.protocolVersion().text()))
.withHeaders(collectHeaders(request.headers()))
.withMethod(HttpMethod.valueOf(request.method().toString().toUpperCase()))
.withUri(decoder.path())
.withQueries(queries)
.withContent(toMessageContent(request))
.build();
}
private static ImmutableMap<String, String[]> toQueries(final QueryStringDecoder decoder) {
ImmutableMap.Builder<String, String[]> builder = ImmutableMap.builder();
for (Map.Entry<String, List<String>> entry : decoder.parameters().entrySet()) {
List<String> value = entry.getValue();
builder.put(entry.getKey(), value.toArray(new String[value.size()]));
}
return builder.build();
}
public FullHttpRequest toFullHttpRequest() {
ByteBuf buffer = Unpooled.buffer();
MessageContent content = getContent();
if (content != null) {
buffer.writeBytes(content.getContent());
}
QueryStringEncoder encoder = new QueryStringEncoder(uri);
for (Map.Entry<String, String[]> entry : queries.entrySet()) {
String[] values = entry.getValue();
for (String value : values) {
encoder.addParam(entry.getKey(), value);
}
}
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.valueOf(getVersion().text()),
io.netty.handler.codec.http.HttpMethod.valueOf(method.name()), encoder.toString(), buffer);
for (Map.Entry<String, String> entry : getHeaders().entrySet()) {
request.headers().add(entry.getKey(), entry.getValue());
}
return request;
}
private static ImmutableMap<String, String> collectHeaders(final Iterable<Map.Entry<String, String>> httpHeaders) {
ImmutableMap.Builder<String, String> headerBuilder = ImmutableMap.builder();
for (Map.Entry<String, String> entry : httpHeaders) {
headerBuilder.put(entry);
}
return headerBuilder.build();
}
public static final class Builder {
private HttpProtocolVersion version;
private MessageContent content;
private ImmutableMap<String, String> headers;
private HttpMethod method;
private String uri;
private ImmutableMap<String, String[]> queries;
public Builder withVersion(final HttpProtocolVersion version) {
this.version = version;
return this;
}
public Builder withTextContent(final String content) {
this.content = content(content);
return this;
}
public Builder withContent(final MessageContent content) {
this.content = content;
return this;
}
public Builder withHeaders(final Map<String, String> headers) {
if (headers != null) {
this.headers = copyOf(headers);
}
return this;
}
public Builder withMethod(final HttpMethod method) {
this.method = method;
return this;
}
public Builder withUri(final String uri) {
this.uri = uri;
return this;
}
public Builder withQueries(final Map<String, String[]> queries) {
if (queries != null) {
this.queries = copyOf(queries);
}
return this;
}
public DefaultHttpRequest build() {
return new DefaultHttpRequest(version, content, method, this.uri, headers, this.queries);
}
}
}